package com.gxc.mall.zuul.filter;

import com.gxc.mall.common.enums.JwtEnum;
import com.gxc.mall.common.util.JwtUtil;
import com.gxc.mall.zuul.util.IpUtil;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

/**
 * 网关.
 *
 * @author GongXincheng
 * @since 2019-09-04 10:08
 */
@Slf4j
@Component
public class MyZuulFilter extends ZuulFilter {

    private static final String LOGIN_URI_STRING = "login";
    private static final String REGISTER_URI_STRING = "register";
    private static final String OPTIONS_REQUEST_METHOD = "OPTIONS";

    @Resource
    private JwtUtil jwtUtil;

    /**
     * 过滤器类型（请求前/后执行）. return "pre"：执行前过滤 return "post"：执行之后
     */
    @Override
    public String filterType() {
        return "pre";
    }

    /**
     * 多个过滤器执行顺序：0：优先. 越小 优先级越高.
     */
    @Override
    public int filterOrder() {
        return 0;
    }

    /**
     * 当前过滤器是否开启：true:开启，false：未开启.
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /**
     * 执行的操作. return 任何Object的值都表示继续执行. setSendZuulResponse(false): 表示不再继续执行
     */
    @Override
    public Object run() throws ZuulException {
        log.info("\r\n ======= MyZuulFilter In ======= \r\n");

        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();
        String token = request.getHeader(JwtEnum.TOKEN_HEADER.getCode());
        String accessToken = request.getParameter(JwtEnum.PARAM_TOKEN.getCode());

        // 注意！放行方法为 “OPTIONS”
        if (request.getMethod().equals(OPTIONS_REQUEST_METHOD)) {
            return null;
        }

        // 校验请求路径(静态资源、登录、注册 放行)
        if (checkRequestUri(request)) {
            return null;
        }

        // 如果未携带token 则访问失败
        String errorMsg = "";
        if (!(StringUtils.isBlank(token) && StringUtils.isBlank(accessToken))) {
            // 判断是否是公用token
            if (checkPublicToken(token, accessToken)) {
                String decodeToken = StringUtils.isNotEmpty(token) ? token : accessToken;
                log.info("public token：[{}] access", decodeToken);
                requestContext.addZuulRequestHeader(JwtEnum.TOKEN_HEADER.getCode(), decodeToken);
                return null;
            }
            // 校验解析token
            try {
                String decodeToken = (StringUtils.isEmpty(token) ? accessToken : token);
                decodeToken = decodeToken.replace(JwtEnum.TOKEN_PREFIX.getCode(), "");

                // 转发请求消息头.
                jwtUtil.parseJwt(decodeToken);
                requestContext.addZuulRequestHeader(JwtEnum.TOKEN_HEADER.getCode(), decodeToken);
                return null;
            } catch (Exception e) {
                errorMsg = e.getMessage();
                log.error("token解析失败, {}", e.getMessage());
                e.printStackTrace();
            }
        } else {
            errorMsg = "无token";
        }
        requestContext.setSendZuulResponse(false);
        requestContext.setResponseStatusCode(401);
        requestContext.setResponseBody("无权限访问：" + errorMsg);
        requestContext.getResponse().setContentType("text/html;charset=utf-8");
        log.error("无权限访问，ip：[{}]，uri：[{}]", IpUtil.getClientIp(request), request.getRequestURI());
        return null;
    }

    /**
     * 判断是否是公用token
     *
     * @param token       Header中的token
     * @param accessToken 请求中的token
     * @return boolean
     */
    private boolean checkPublicToken(String token, String accessToken) {
        return jwtUtil.getToken().contains(token) ||
                jwtUtil.getToken().contains(accessToken);
    }

    /**
     * 校验请求uri是否需要被拦截.
     */
    private boolean checkRequestUri(HttpServletRequest request) {
        String requestUri = request.getRequestURI().toLowerCase();
        // 如果是 登录 和 注册 请求放行.
        return requestUri.indexOf(LOGIN_URI_STRING) > 0 || requestUri.indexOf(REGISTER_URI_STRING) > 0;
    }

    /**
     * 获取 request 对象.
     */
    private HttpServletRequest getRequest() {
        RequestContext currentContext = RequestContext.getCurrentContext();
        return currentContext.getRequest();
    }
}
